package jastgen.generator;

import static jastgen.generator.Generator.unique;
import static jastgen.generator.Helper.*;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import jastgen.nodes.*;

public class DefaultContextBodyGenerator implements BodyGenerator {

	private static final String
	strContext = "context";

	private final List<ClassDef> classDefs;
	private final String uniqueContext;

	DefaultContextBodyGenerator(
			final List<ClassDef> classDefs)
	{
		this.classDefs = classDefs;
		uniqueContext = unique(getAllFields(classDefs), strContext);
	}

	// 全クラスの全フィールド
	private static List<Field> getAllFields(
			final List<ClassDef> cs)
	{
		final List<Field> fs = new ArrayList<Field>();  // TODO: size
		for(final ClassDef c : cs)
			fs.addAll(c.getFields());
		return fs;
	}

	@Override
	public void generate(final PrintWriter out) {

		out.println("public final class DefaultContext {");
		out.println();

		out.println("	private DefaultContext() {");
		out.println("	}");
		out.println();
		out.println("	private static final Context " + uniqueContext + " = new Context();");
		out.println();
		out.println("	public static Context context() {");
		out.println("		return " + uniqueContext + ";");
		out.println("	}");
		out.println();

		// 最初に定義されたクラスをrootノードの型とする
		final String cn = classDefs.get(0).getInterfaceOrClassBaseName();

		out.println("	public static void purgeUnusedNodes(");
		out.println("			final " + cn + " root)");
		out.println("	{");
		out.println("		" + uniqueContext + ".purgeUnusedNodes(root);");
		out.println("	}");

		printFactory(out);

		out.println("}");
	}

	private void printFactory(final PrintWriter out) {

		for(final ClassDef c : classDefs)
			printFactory(out, c);
	}

	private void printFactory(
			final PrintWriter out,
			final ClassDef node)
	{
		final String n = node.getBaseName();
		final String ifn = node.getInterfaceOrClassName();

		out.println();
		out.println("	/**");
		out.println("	 * Factory method of '" + n + "' node.");
		// out.println("	 * @param ${FIELD_NAME} Element of return value.");
		out.println("	 * @return Instance of '" + n + "' class.");
		out.println("	 */");

		out.print("	public static ");
		out.print(node.getTypeParamsString());
		out.print(ifn + " " + n + "(");

		printContextFactoryParams(out, node);

		out.println(")");
		out.println("	{");
		out.print("		return " + uniqueContext + ".get" + n + "(");

		printContextFactoryArgs(out, node, true);

		out.println(");");
		out.println("	}");
	}
}
